#include"DataDecoding.h"
static enum AVPixelFormat hw_pix_fmt;
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts)
{
    const enum AVPixelFormat *p;

    for (p = pix_fmts; *p != -1; p++)
    {
        if (*p == hw_pix_fmt)
            return *p;
    }
    fprintf(stderr, "Failed to get HW surface format.\n");
    return AV_PIX_FMT_NONE;
}

DataDecoding::DataDecoding()
{
    pool = new HzThreadPool(10);
    useHw = true;
}

DataDecoding::DataDecoding(enum AVCodecID id,RecFrameServer* ecvFrameServer):
    id(id),
    ecvFrameServer(ecvFrameServer)
{
    pool = new HzThreadPool(10);
    useHw = true;
}

DataDecoding::~DataDecoding()
{
    delete pool;
}

int DataDecoding::init_decode_core()
{
    cout<<"init decoder..."<<endl;
    // 1 确定硬件类型
    if(useHw){
        type = av_hwdevice_find_type_by_name("cuda");
        if (type == AV_HWDEVICE_TYPE_NONE)
        {
            fprintf(stderr, "Device type %s is not supported.\n", "cuda");
            fprintf(stderr, "Available device types:");
            while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
                fprintf(stderr, " %s", av_hwdevice_get_type_name(type));
            fprintf(stderr, "\n");
            return -1;
        }
    }

    // 2.寻找解码器  AV_CODEC_ID_H264
    codec = avcodec_find_decoder(id);
    if (!codec)
    {
        cout << "无法找到H.264解码器" << endl;
        return -1;
    }

    // 3 获取硬件解码器在特定索引（i）下的硬件配置信息。
    if(useHw){
        for (int i = 0;; i++)
        {
            const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
            // cout << "config->pix_fmt:" << config->pix_fmt << endl; // AV_PIX_FMT_CUDA
            // exit(0);
            if (!config)
            {
                fprintf(stderr, "Decoder %s does not support device type %s.\n",
                        codec->name, av_hwdevice_get_type_name(type));
                return -1;
            }
            if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
                config->device_type == type)
            {
                hw_pix_fmt = config->pix_fmt;
                break;
            }
        }
    }

    // 4.初始化解码处理器
    parser = av_parser_init(codec->id);
    if (!parser)
    {
        cout << "无法初始化处理器" << endl;
        return -1;
    }

    // 5 分派上下文
    codecContext = avcodec_alloc_context3(codec);
    if (!codecContext)
    {
        cout << "无法分配解码器上下文" << endl;
        return -1;
    }

    // 5 设置像素格式的回调函数。这个回调函数负责为硬件加速解码器提供适当的像素格式。
    if(useHw) codecContext->get_format = get_hw_format;

    /** 6
     * 创建一个硬件设备上下文   AVHWDeviceContext
     * 然后将其关联到 AVCodecContext 中的 hw_device_ctx 字段。
     * 在硬件加速解码时，这个硬件设备上下文用于指定解码器使用的硬件设备。
     */
    if(useHw){
        int err;
        if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type,
                                          NULL, NULL, 0)) < 0)
        {
            fprintf(stderr, "Failed to create specified HW device.\n");
            return err;
        }
        codecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx);
    }

    //8  打开编码器
    if (avcodec_open2(codecContext, codec, nullptr) < 0)
    {
        cout << "无法打开解码器\n";
        return -1;
    }

    //9 创建AVPacket并分配内存
    pkt = av_packet_alloc();
    if (!pkt)
    {
        cout << "创建AVPacket错误" << endl;
    }

    //10 创建AVFrame并分配内存
    frame = av_frame_alloc();
    sw_frame = av_frame_alloc();
    tem_frame = av_frame_alloc();
    if (!frame || !sw_frame || !tem_frame)
    {
        cout << "无法分配AVFrame\n";
        return -1;
    }
    cout << "init complete!" << endl;
    if (!frame)
    {
        cout << "无法分配AVFrame\n";
        return -1;
    }
    cout << "init decode complete!" << endl;
    return 0;
}

int DataDecoding::release_decode_core()
{
    cout<<"release decode core..."<<endl;
    if(useHw) av_buffer_unref(&hw_device_ctx);
    av_parser_close(parser);
    avcodec_free_context(&codecContext);
    //av_frame_free(&frame);
    //av_frame_free(&sw_frame);
    av_frame_free(&tem_frame);
    av_packet_free(&pkt);
    cout<<"release decode core successfully!"<<endl;
}

int DataDecoding::run()
{
    init_decode_core();
    // 1 新开线程将帧原始数据送入上下文
    std::thread thread1(&DataDecoding::start_decode, this);
    // 2 循环获取解码结果
    int i = 0;
    while (1)
    {
        int ret = this->recv_the_frame();
        if (ret == 0)
        {
            //cout<<"recv frame .............."<<endl;
            i++;
            if(tem_frame->format == AV_PIX_FMT_NV12){
                YuvBuffer* yuvBuffer = new YuvBuffer(tem_frame->linesize[0] * tem_frame->height,
                                                                                               tem_frame->linesize[0] * tem_frame->height/4,
                                                                                               tem_frame->linesize[0] * tem_frame->height/4);
                memcpy(yuvBuffer->y, tem_frame->data[0], tem_frame->linesize[0] * tem_frame->height);
                int uIndex = 0;
                for (int i = 0; i < tem_frame->linesize[0] * tem_frame->height / 2; i += 2)
                {
                    yuvBuffer->u[uIndex++] = tem_frame->data[1][i];
                }
                uIndex = 0;
                for (int i = 1; i < tem_frame->linesize[0]  * tem_frame->height / 2; i += 2)
                {
                    yuvBuffer->v[uIndex++] = tem_frame->data[1][i];
                }
                //cout<<"push  push"<<endl;
                RecYuvstreams.Push(yuvBuffer);
            }
        }
        else if (ret == -1)
        { // 读取发生错误
            release_decode_core();
            return -1;
        }
        else if (ret == AVERROR_EOF)
            break;
    }
    thread1.join();
    release_decode_core();
}

void DataDecoding::start_decode()
{
    EncodeBuffer* encodeBuffer;
    while(ecvFrameServer->status){
            if(encodeStreams.Size() == 0){
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
                continue;
            }
            // 1 开启获取缓冲区的yuv444数据
            encodeBuffer = encodeStreams.Front();
            encodeStreams.Pop();
            // 2 存入结构
            pkt->data = encodeBuffer->encoderFramePtr;
            pkt->size = encodeBuffer->size;
//            int ret = av_parser_parse2(parser, codecContext, &pkt->data, &pkt->size,
//                                       encodeBuffer->encoderFramePtr, encodeBuffer->size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
//            cout<<"av_parser_parse2 ret:"<<ret<<endl;
            // 3 发送到上下文
            send_a_pkt(false);
            delete encodeBuffer;
        }
        send_a_pkt(true);
}

int DataDecoding::send_a_pkt(bool flush)
{
    int ret = 0;
    readContextMutex.lock();
    ret = avcodec_send_packet(codecContext, flush ? nullptr : pkt);
    readContextMutex.unlock();
    //cout<<"avcodec_send_packet:"<<ret<<endl;
    switch (ret)
    {
    case 0:
        if (flush)
            //cout << "send frame to encoder with pts:" << frame->pts << endl;
            cout << "send pkt end!!" << endl;
        break;
    case AVERROR(EAGAIN):
        //cout << "缓冲区已满，请先通过avcodec_receive_frame读出" << endl;
        break;
    default:
        cout << "erreo : send pkt erro！" << endl;
        break;
    }
    return ret;
}

int DataDecoding::recv_the_frame()
{
    // 1 尝试获取解码后的结果
     readContextMutex.lock();
    int ret = avcodec_receive_frame(codecContext, frame);
    readContextMutex.unlock();
    // 2 判断获取结果
    switch (ret)
    {
    case AVERROR(EAGAIN): // "AVERROR_EOF:上下文中没有码流,输出结束；AVERROR(EAGAIN)：输出不可用\n";
        // cout<<"输出不可用"<<endl;
        break;
    case AVERROR_EOF:
        // cout<<"输出结束"<<endl;
        break;
    case 0: // 成功获取一帧解码后的码流
            // cout << "get encoded package with dts:" << pkt->dts << ",pts:" << pkt->pts << endl;
        break;
    default:
        cout << "erreo : get pkt fail!!" << endl;
        break;
    }
    if(useHw){
        if (frame->format == hw_pix_fmt)
           {
               /* retrieve data from GPU to CPU */
               if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0)
               {
                   fprintf(stderr, "Error transferring the data to system memory\n");
                   return -1;
               }
               tem_frame = sw_frame;
           }
           else
               tem_frame = frame;
    }else{
        tem_frame = frame;
    }
    return ret;
}


